package com.cpxj.util;

import com.github.rholder.retry.*;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;

@Slf4j
public class GuavaRetryUtil {

    /**
     * 固定时间重试，重试异常和重试条件满足其一就会发生重试
     * @param function 执行方法
     * @param t 入参
     * @param exceptionClass 重试异常
     * @param resultPredicate 重试条件
     * @param maxRetryCount 最大重试次数
     * @param retryTime 重试间隔时间
     * @param <T>
     * @param <R>
     * @return
     * @throws ExecutionException
     * @throws RetryException
     */
    public static <T, R> R retryFixedTime(Function<T, R> function, T t, Class<? extends Throwable> exceptionClass, Predicate<R> resultPredicate, int maxRetryCount, int retryTime) throws ExecutionException, RetryException {
        try {
            R r =
                    RetryerBuilder.<R>newBuilder()
                            .retryIfExceptionOfType(exceptionClass)
                            .retryIfResult(resultPredicate::test)
                            .withWaitStrategy(WaitStrategies.fixedWait(retryTime, TimeUnit.MILLISECONDS))
                            .withStopStrategy(StopStrategies.stopAfterAttempt(maxRetryCount))
                            .withBlockStrategy(BlockStrategies.threadSleepStrategy())
                            .withRetryListener(new MyRetryListener())
                            .build()
                            .call(() -> function.apply(t));
            return r;
        } catch (ExecutionException e) {
            log.error("retry ExecutionException", e);
            throw e;
        } catch (RetryException e) {
            log.error("retry RetryException", e);
            throw e;
        }
    }


    /**
     * 时间递增重试，重试异常和重试条件满足其一就会发生重试
     * @param function 执行方法
     * @param t 入参
     * @param exceptionClass 重试异常
     * @param predicate 重试条件
     * @param maxRetryCount 最大重试次数
     * @param initialSleepTime 初始重试时间
     * @param incrementSleepTime 递增重试时间
     * @param <T>
     * @param <R>
     * @return
     * @throws ExecutionException
     * @throws RetryException 达到最大重试次数之后仍然失败会抛这个异常
     */
    public static <T, R> R retryIncrementTime(Function<T, R> function, T t, Class<? extends Throwable> exceptionClass, Predicate<R> predicate, int maxRetryCount, int initialSleepTime, int incrementSleepTime) throws ExecutionException, RetryException {
        try {
            R r =
                    RetryerBuilder.<R>newBuilder()
                            .retryIfExceptionOfType(exceptionClass)
                            .retryIfResult(predicate::test)
                            .withWaitStrategy(WaitStrategies.incrementingWait(initialSleepTime, TimeUnit.MILLISECONDS, incrementSleepTime, TimeUnit.MILLISECONDS))
                            .withStopStrategy(StopStrategies.stopAfterAttempt(maxRetryCount))
                            .withBlockStrategy(BlockStrategies.threadSleepStrategy())
                            .withRetryListener(new MyRetryListener())
                            .build()
                            .call(() -> function.apply(t));
            return r;
        } catch (ExecutionException e) {
            log.error("retry ExecutionException", e);
            throw e;
        } catch (RetryException e) {
            log.error("retry RetryException", e);
            throw e;
        }
    }


    static class MyRetryListener implements RetryListener {
        @Override
        public <V> void onRetry(Attempt<V> attempt) {
            log.info("重试次数：{}",attempt.getAttemptNumber());
        }
    }


    public static void main(String[] args) throws Exception{
        Integer integer = retryFixedTime(new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer integer) {
                return realAction(integer);
            }
        }, 5, Exception.class, new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return true;
            }
        }, 3, 200);
        System.out.println("---------"+integer+"-----------");
    }

   /* public static void main(String[] args) throws Exception{
        Integer integer = retryIncrementTime(new Function<Integer, Integer>() {
            @Override
            public Integer apply(Integer integer) {
                return realAction(integer);
            }
        }, 4, IllegalArgumentException.class, new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer % 2 == 0;
            }
        }, 3, 100,200);
        System.out.println("---------"+integer+"-----------");
    }*/


    public static int realAction(int num) {
        System.out.println("执行中。。。。。。");
        return num;
    }
}
